#pragma once
#include "../logger.hpp"
#include "user.hxx"
#include "user-odb.hxx"
#include "user.pb.h"
#include <algorithm>
#include <cstdlib> 
#include <iostream>
#include <memory>
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include <sstream>
#include <string>

namespace shared {

namespace dal {

class UserDao;
using userDaoPtr = std::shared_ptr<UserDao>;

class UserDao
{
    using query = odb::query<entity::User>;
    using UserQueryMaker = std::function<query(const std::string&)>;
    using UserPtr = entity::UserPtr;
public:
    UserDao(const std::shared_ptr<odb::mysql::database>& db) : db_(db) {}

    bool insert(const std::shared_ptr<entity::User>& user)
    {
        try {
            // 开始事务 - 相当于 start transaction
            odb::transaction trans(db_->begin());
            // 插入数据 
            db_->persist(*user);
            trans.commit();
        } catch (const std::exception& e) {
            LOG_ERROR("新增用户({})失败: {}", user->userId(), e.what());
            return false;
        }
        return true;
    }

    bool update(const std::shared_ptr<entity::User>& user)
    {
        try {
            // 开始事务 - 相当于 start transaction
            odb::transaction trans(db_->begin());
            // 更新数据
            db_->update(*user);
            trans.commit();
        } catch (const std::exception& e) {
            LOG_ERROR("更新用户({})失败: {}", user->userId(), e.what());
            return false;
        }
        return true;
    }

    UserPtr selectByNickname(const std::string& nickname) 
    {
        return selectUser("nickname", nickname, [&](const std::string& val){
            return odb::query<entity::User>::nickname == val;
        });
    }
    UserPtr selectByPhone(const std::string& phone) 
    {
        return selectUser("phone", phone, [&](const std::string& val){
            return odb::query<entity::User>::phone == val;
        });
    }

    UserPtr selectByUserId(const std::string& userId) 
    {
        return selectUser("userId", userId, [&](const std::string& val){
            return odb::query<entity::User>::userId == val;
        });
    }
    std::vector<UserPtr> selectMultiUsers(const std::vector<std::string>& userIdList) 
    {
        if(userIdList.empty()) {
            return std::vector<UserPtr>();
        }        
        std::vector<UserPtr> selectUsers;
        try {
            odb::transaction trans(db_->begin());
            typedef odb::result<entity::User> result;

            std::stringstream ss;
            ss << "user_id in(";
            for(const auto& id : userIdList) {
                ss << "'" << id << "',";
            }
            std::string condition = ss.str();
            condition.pop_back();
            condition += ')';

            result ret(db_->query<entity::User>(condition));
            for(auto it = ret.begin(); it != ret.end(); ++it) {
                selectUsers.emplace_back(std::make_shared<entity::User>(*it));
            }
            trans.commit();
        } catch (const std::exception& e) {
            LOG_ERROR("通过userId批量查询用户失败: {}", e.what());
        }
        return selectUsers;
    }
    std::vector<UserPtr> selectMultiUsers(const google::protobuf::RepeatedPtrField<std::string>& userIdList) 
    {
        if(userIdList.empty()) {
            return std::vector<UserPtr>();
        }        
        
        std::vector<UserPtr> selectUsers;
        try {
            odb::transaction trans(db_->begin());
            typedef odb::result<entity::User> result;

            std::stringstream ss;
            ss << "user_id in(";
            for (const auto& id : userIdList) {
                ss << "'" << id << "',";
            }
            std::string condition = ss.str();
            condition.pop_back();
            condition += ')';

            result ret(db_->query<entity::User>(condition));
            for(auto it = ret.begin(); it != ret.end(); ++it) {
                selectUsers.emplace_back(std::make_shared<entity::User>(*it));
            }
            trans.commit();
        } catch (const std::exception& e) {
            LOG_ERROR("通过userId批量查询用户失败: {}", e.what());
        }
        return selectUsers;
    }
private:
    UserPtr selectUser(const std::string& key, const std::string& val, const UserQueryMaker& queryMaker) 
    {
        std::shared_ptr<entity::User> res;
        try {
            odb::transaction trans(db_->begin());
            res.reset(db_->query_one<entity::User>(queryMaker(val)));
            trans.commit();
        } catch (const std::exception& e) {
            LOG_ERROR("通过{}查询用户({})失败: {}", key, val, e.what());
            return res;
        }
        return res;
    }
private:
    std::shared_ptr<odb::mysql::database> db_;
};


}}